﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Diagnostics;
using System.ComponentModel;
using FinPlusCommon;
using log = Logger.Logger;
using u = FinPlusUtility.Utility;

namespace FinPlusComponents
{
    public class FinPlusQuoteEngine : FinPlusComponent
    {
        public override string Id { get; set; }
        public override string[] States { get { return new string[] { }; } }
        public override string[] TestStates { get { return new string[] { "ModelTest", "IsValid" }; } }
        public override IFinPlusFunctions Model { get { return _instrumentConn.Model; } }
        public override string ModelTest { get { return Model.ToString(); } }
        public override bool IsValid { get; set; }

        private enum DockControls { FinPlusDataConn, FinPlusChildService, Default }
        private enum Connection { Instruments, Spreads, SaveCloses, QuoteInterests, QuoteSource, Tag, Counterpartys, CounterpartyTiers, TradeAdaptors, QuoteAdaptors, Exception }
        private enum Params { Instrument, InstrumentType, Counterparty, CurveName, DefaultCollateralCurve, FunctionName, QuoteInstrument, QuoteSource, ParseTradeAction, ReceiveAction, PublishAction, ResponseAction,Tag }
        
        private IFinPlusComponent _instrumentConn, _spreadConn, _quoteInterestConn, _counterpartyConn,  _counterpartyTierConn, _quoteAdaptorConn, _tradeAdaptorConn;
        private FinPlusDataConn _saveClosesConn;
        private readonly FinPlusQuoteInstruments _quoteInstruments;
        private readonly ConcurrentDictionary<string, IFinPlusFunction> _quoteAdaptors;
        private readonly ConcurrentDictionary<string, FinPlusQuoteWrapper> _quotes, _publishQuotes;
        private bool _hasInitialised;

        //construct
        public FinPlusQuoteEngine()
        {
            _quoteInstruments = new FinPlusQuoteInstruments();
            _quotes = new ConcurrentDictionary<string, FinPlusQuoteWrapper>();
            _publishQuotes = new ConcurrentDictionary<string, FinPlusQuoteWrapper>();
            _quoteAdaptors = new ConcurrentDictionary<string, IFinPlusFunction>();
        }

        //common control interface
        public override void Dock(object component, string dock)
        {
            try
            {
                switch (u.EnumTryParse<DockControls>(component.GetType().Name, DockControls.Default))
                {
                    case DockControls.FinPlusChildService:
                    case DockControls.FinPlusDataConn:
                    case DockControls.Default: AddConnection((IFinPlusComponent)component, dock); break;
                }
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        public override void Initialise()
        {
            try
            {
                if (_hasInitialised) return;

                //rename instruments
                var counterpartyCollateralCurves = _counterpartyConn.Model.ToDictionary(Params.Counterparty.ToString(), Params.DefaultCollateralCurve.ToString());
                foreach (var instrument in _instrumentConn.Model.Values)
                    instrument.Get(Params.FunctionName.ToString()).Set(instrument.Name = Params.QuoteInstrument.ToString());

                //add ecns
                foreach (var func in _quoteAdaptorConn.Model.Values)
                {
                    _quoteAdaptors[func[Params.QuoteSource.ToString()].ToString()] = func;
                    func.Get(Params.ReceiveAction.ToString()).PropertyChanged += Quote_Receive;
                }

                //build instruments
                foreach (var instrument in _instrumentConn.Model.Values)
                    _quoteInstruments[instrument[Params.Instrument.ToString()].ToString()] = new FinPlusQuoteInstrument(instrument, counterpartyCollateralCurves, _quoteAdaptors.Values.Select(f => f[Params.QuoteSource.ToString()].ToString()).ToList<string>(), Quote_Publish);

                //add spreads
                foreach (var quoteInstrument in _quoteInstruments.Values)
                    quoteInstrument.AddSpreads(_spreadConn.Model.ToArray(Params.Instrument.ToString(), quoteInstrument.Instrument), _quoteInstruments);

                //add quote interests
                foreach (var quoteInstrument in _quoteInstruments.Values)
                    quoteInstrument.AddQuoteInterests(_quoteInterestConn.Model.ToArray(Params.Instrument.ToString(), quoteInstrument.Instrument));


                //get trades back after quote parse for testing
                foreach (var tradeAdaptor in _tradeAdaptorConn.Model.Values)
                    tradeAdaptor.Get(Params.ResponseAction.ToString()).PropertyChanged += Trade_Done;

                IsValid = _hasInitialised = true;
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        public override void CommandRouter(IFinPlusFunction func)
        {
            try
            {
                CaptureCommand(func);

                //SaveAudit(func.Name, new IFinPlusFunction[] { func });

                switch ((Commands)Enum.Parse(typeof(Commands), func.Name, true))
                {
                    case Commands.SaveCloses: SaveCloses(func); break;
                    case Commands.Quote: Quote(func); break;
                    case Commands.QuoteCancel: QuoteCancel(func); break;
                    case Commands.QuoteExecute: QuoteExecute(func); break;
                    case Commands.QuoteMissed: QuoteMissed(func); break;
                    case Commands.QuoteAccept: QuoteAccept(func); break;
                    case Commands.QuoteDecline: QuoteDecline(func); break;
                    case Commands.QuotePublish: QuotePublish(func); break;
                    case Commands.QuoteCancelPublish: QuoteCancelPublish(func); break;
                    case Commands.QuoteAcceptPublish: QuoteAcceptPublish(func); break;
                    case Commands.QuoteDeclinePublish: QuoteDeclinePublish(func); break;
                    case Commands.QuoteExecutePublish: QuoteExecutePublish(func); break;
                    case Commands.BookedTrade: BookedTrade(func); break;
                    default: throw new Exception(string.Format("event not recognised {0}", func.Name));
                }
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        public override void Dispose()
        {
            try
            {
                if (_quoteInstruments != null)
                    _quoteInstruments.Dispose();
                if (_instrumentConn != null)
                    _instrumentConn.Dispose();
                if (_saveClosesConn != null)
                    _saveClosesConn.Dispose();
                if (_spreadConn != null)
                    _spreadConn.Dispose();
                if (_quoteInterestConn != null)
                    _quoteInterestConn.Dispose();
                if (_counterpartyConn != null)
                    _counterpartyConn.Dispose();
                if (_counterpartyTierConn != null)
                    _counterpartyTierConn.Dispose();
                if (_quoteAdaptorConn != null)
                    _quoteAdaptorConn.Dispose();
                if (_tradeAdaptorConn != null)
                    _tradeAdaptorConn.Dispose();
            }
            catch (Exception e)
            {
                log.Log(Logger.Level.Warning, e.Message, e);
            }
        }

        //private
        private void AddConnection(IFinPlusComponent conn, string dock)
        {
            switch (u.EnumTryParse<Connection>(dock, Connection.Exception))
            {
                case Connection.Instruments: _instrumentConn = conn; break;
                case Connection.QuoteInterests: _quoteInterestConn = conn; break;
                case Connection.SaveCloses: _saveClosesConn = (FinPlusDataConn)conn; break;
                case Connection.Spreads: _spreadConn = conn; break;
                case Connection.Counterpartys: _counterpartyConn = conn; break;
                case Connection.CounterpartyTiers: _counterpartyTierConn = conn; break;
                case Connection.QuoteAdaptors: _quoteAdaptorConn = conn; break;
                case Connection.TradeAdaptors: _tradeAdaptorConn = conn; break;
                case Connection.Exception: throw new NotImplementedException();
            }
        }

        private void SaveCloses(IFinPlusFunction func)
        {
            if(_saveClosesConn != null)
                foreach (var quoteInstrument in _quoteInstruments.Values)
                    foreach (var buySell in quoteInstrument.Trader.BuySells.Values)
                        _saveClosesConn.Save(buySell.MarketLevel);
        }

        private void Quote(IFinPlusFunction func)
        {
            try
            {
                var quote = new FinPlusQuoteWrapper(func);
                FinPlusQuoteInstrument instrument;
                if (_quoteInstruments.TryGetValue(quote.Instrument, out instrument) && _quotes.TryAdd(quote.QuoteRef, quote))
                    instrument.Quote(quote);
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void QuoteCancel(IFinPlusFunction func)
        {
            try
            {
                var cancel = new FinPlusQuoteWrapper(func);
                FinPlusQuoteWrapper quote; 
                FinPlusQuoteInstrument instrument;
                if (_quotes.TryRemove(cancel.QuoteRef, out quote) && _quoteInstruments.TryGetValue(quote.Instrument, out instrument))
                    instrument.QuoteCancel(quote);
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void QuoteExecute(IFinPlusFunction func)
        {
            try
            {
                var quoteExectue = new FinPlusQuoteWrapper(func);
                IFinPlusFunction quoteAdaptor;
                if (_quotes.ContainsKey(quoteExectue.QuoteRef) && _quoteAdaptors.TryGetValue(quoteExectue.SourceName, out quoteAdaptor))
                {
                    quoteAdaptor.Get(Params.PublishAction.ToString()).Set(quoteExectue.Quote);
                }
                else
                {
                    func.Get(Params.FunctionName.ToString()).Set(func.Name = Commands.QuoteMissed.ToString());
                    Quote_Publish(func);
                }
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void QuoteMissed(IFinPlusFunction func)
        {
            try
            {
                var quoteMissed = new FinPlusQuoteWrapper(func);
                FinPlusQuoteInstrument instrument;
                if (_quoteInstruments.TryGetValue(quoteMissed.Instrument, out instrument))
                    instrument.QuoteMissed(quoteMissed);
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void QuoteDecline(IFinPlusFunction func)
        {
            try
            {
                var quoteDecline = new FinPlusQuoteWrapper(func);
                FinPlusQuoteInstrument instrument;
                if (_quoteInstruments.TryGetValue(quoteDecline.Instrument, out instrument))
                    instrument.QuoteDecline(quoteDecline);
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void QuoteAccept(IFinPlusFunction func)
        {
            try
            {
                var quoteAccept = new FinPlusQuoteWrapper(func);
                FinPlusQuoteInstrument instrument;
                if (_quoteInstruments.TryGetValue(quoteAccept.Instrument, out instrument))
                {
                    foreach (var tradeAdaptor in _tradeAdaptorConn.Model.Values.Where(f => f[Params.InstrumentType.ToString()].Equals(quoteAccept.InstrumentType)))
                        tradeAdaptor.Get(Params.ParseTradeAction.ToString()).Set(quoteAccept.Quote);//to trade engine
                    
                    instrument.QuoteAccept(quoteAccept);
                }
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void QuotePublish(IFinPlusFunction func)
        {
            try
            {
                var quotePublish = new FinPlusQuoteWrapper(func);
                IFinPlusFunction quoteAdaptor;
                if (_quoteAdaptors.TryGetValue(quotePublish.SourceName, out quoteAdaptor) && _publishQuotes.TryAdd(quotePublish.QuoteRef, quotePublish))
                    quoteAdaptor.Get(Params.PublishAction.ToString()).Set(quotePublish.Quote);
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void QuoteCancelPublish(IFinPlusFunction func)
        {
            try
            {
                var cancelPublish = new FinPlusQuoteWrapper(func);
                IFinPlusFunction quoteAdaptor;
                if (_quoteAdaptors.TryGetValue(cancelPublish.SourceName, out quoteAdaptor))
                    quoteAdaptor.Get(Params.PublishAction.ToString()).Set(cancelPublish.Quote);
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void QuoteExecutePublish(IFinPlusFunction func)
        {
            try
            {
                var execute = new FinPlusQuoteWrapper(func);
                FinPlusQuoteWrapper quote; FinPlusQuoteInstrument instrument;
                if (_publishQuotes.TryGetValue(execute.QuoteRef, out quote) && _quoteInstruments.TryGetValue(quote.Instrument, out instrument))
                    instrument.QuoteExecutePublish(execute);
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void QuoteDeclinePublish(IFinPlusFunction func)
        {
            try
            {
                var quoteDeclinePublish = new FinPlusQuoteWrapper(func);
                FinPlusQuoteWrapper quote; IFinPlusFunction quoteAdaptor;
                if (_publishQuotes.TryRemove(quoteDeclinePublish.QuoteRef, out quote) && _quoteAdaptors.TryGetValue(quoteDeclinePublish.SourceName, out quoteAdaptor))
                    quoteAdaptor.Get(Params.PublishAction.ToString()).Set(quoteDeclinePublish.Quote); //to ecn
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void QuoteAcceptPublish(IFinPlusFunction func)
        {
            try
            {
                var quoteAcceptPublish = new FinPlusQuoteWrapper(func);
                FinPlusQuoteWrapper quote;
                IFinPlusFunction quoteAdaptor;
                FinPlusQuoteInstrument instrument;
                if (_quoteInstruments.TryGetValue(quoteAcceptPublish.Instrument, out instrument) && _publishQuotes.TryRemove(quoteAcceptPublish.QuoteRef, out quote) && _quoteAdaptors.TryGetValue(quoteAcceptPublish.SourceName, out quoteAdaptor))
                {
                    foreach (var tradeAdaptor in _tradeAdaptorConn.Model.Values.Where(f => f[Params.InstrumentType.ToString()].Equals(quoteAcceptPublish.InstrumentType)))
                        tradeAdaptor.Get(Params.ParseTradeAction.ToString()).Set(quoteAcceptPublish.Quote); //to trade engine

                    quoteAdaptor.Get(Params.PublishAction.ToString()).Set(quoteAcceptPublish.Quote); //to ecn
                }
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void BookedTrade(IFinPlusFunction func)
        {
            try
            {
                var trade = Function.FromString(func[Params.Tag.ToString()].ToString());
                var item = trade.Get(Params.Instrument.ToString());
                trade.Remove(item);
                FinPlusQuoteInstrument instrument;
                if (_quoteInstruments.TryGetValue(item.Value, out instrument))
                    instrument.BookedTrade(trade);
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        //cmds 
        private new enum Commands { SaveCloses, Quote, QuoteCancel, QuoteExecute, QuoteMissed, QuoteDecline, QuoteAccept, QuotePublish, QuoteCancelPublish, QuoteExecutePublish, QuoteAcceptPublish, QuoteDeclinePublish, BookedTrade }

        //events
        private void Quote_Receive(object sender, PropertyChangedEventArgs a) { CommandRouter((Function)((IFinPlusItem)sender).Object); }
        private void Quote_Publish(IFinPlusFunction func) { CommandRouter(func); }
        private void Trade_Done(object sender, PropertyChangedEventArgs a) { CommandRouter(new Function(Commands.BookedTrade.ToString(), Id, User, -1, ((IFinPlusItem)sender).Value)); }
    }
}

